/*jslint browser: true, nomen: false, plusplus: false, bitwise: false, maxerr: 50, indent: 2 */
/**
 * @depends swfobject-2.2.min.js
 *
 * evercookie 0.4 (10/13/2010) -- extremely persistent cookies
 *
 *  by samy kamkar : code@samy.pl : http://samy.pl
 *
 * this api attempts to produce several types of persistent data
 * to essentially make a cookie virtually irrevocable from a system
 *
 * specifically it uses:
 *  - standard http cookies
 *  - flash cookies (local shared objects)
 *  - silverlight isolated storage
 *  - png generation w/forced cache and html5 canvas pixel reading
 *  - http etags
 *  - http cache
 *  - window.name
 *  - IE userData
 *  - html5 session cookies
 *  - html5 local storage
 *  - html5 global storage
 *  - html5 database storage via sqlite
 *  - css history scanning
 *  - Java JNLP PersistenceService
 *  - Java exploit
 *
 *  if any cookie is found, it's then reset to all the other locations
 *  for example, if someone deletes all but one type of cookie, once
 *  that cookie is re-discovered, all of the other cookie types get reset
 *
 *  !!! SOME OF THESE ARE CROSS-DOMAIN COOKIES, THIS MEANS
 *  OTHER SITES WILL BE ABLE TO READ SOME OF THESE COOKIES !!!
 *
 * USAGE:
  var ec = new evercookie();
  // set a cookie "id" to "12345"
  // usage: ec.set(key, value)
  ec.set("id", "12345");
  // retrieve a cookie called "id" (simply)
  ec.get("id", function(value) { alert("Cookie value is " + value) });
  // or use a more advanced callback function for getting our cookie
  // the cookie value is the first param
  // an object containing the different storage methods
  // and returned cookie values is the second parameter
  function getCookie(best_candidate, all_candidates)
  {
    alert("The retrieved cookie is: " + best_candidate + "\n" +
      "You can see what each storage mechanism returned " +
      "by looping through the all_candidates object.");
    for (var item in all_candidates)
      document.write("Storage mechanism " + item +
        " returned " + all_candidates[item] + " votes<br>");
    }
    ec.get("id", getCookie);
  // we look for "candidates" based off the number of "cookies" that
  // come back matching since it's possible for mismatching cookies.
  // the best candidate is very-very-likely the correct one
*/
try{
    (function (window) {
      'use strict';
      var document = window.document,
        Image = window.Image,
        globalStorage = window.globalStorage,
        swfobject = window.swfobject;
    
      try{
        var localStore = window.localStorage
      }catch(ex){}
      
      try {
        var sessionStorage = window.sessionStorage;
      } catch (e) { }
    
      function newImage(src) {
        var img = new Image();
        img.style.visibility = "hidden";
        img.style.position = "absolute";
        img.src = src;
      }
      function _ec_replace(str, key, value) {
        if (str.indexOf("&" + key + "=") > -1 || str.indexOf(key + "=") === 0) {
          // find start
          var idx = str.indexOf("&" + key + "="),
            end, newstr;
          if (idx === -1) {
            idx = str.indexOf(key + "=");
          }
          // find end
          end = str.indexOf("&", idx + 1);
          if (end !== -1) {
            newstr = str.substr(0, idx) + str.substr(end + (idx ? 0 : 1)) + "&" + key + "=" + value;
          } else {
            newstr = str.substr(0, idx) + "&" + key + "=" + value;
          }
          return newstr;
        } else {
          return str + "&" + key + "=" + value;
        }
      }
    
     function idb() {
        if ('indexedDB' in window) {
            return true
        } else if (window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) {
            return true
        } else {
            return false
        }
      } 
    
      // necessary for flash to communicate with js...
      // please implement a better way
      var _global_lso;
      function _evercookie_flash_var(cookie) {
        _global_lso = cookie;
    
        // remove the flash object now
        var swf = document.getElementById("myswf");
        if (swf && swf.parentNode) {
          swf.parentNode.removeChild(swf);
        }
      }
    
      /*
       * Again, ugly workaround....same problem as flash.
       */
      var _global_isolated;
      function onSilverlightLoad(sender, args) {
        var control = sender.getHost();
        _global_isolated = control.Content.App.getIsolatedStorage();
      }
    
      function onSilverlightError(sender, args) {
        _global_isolated = "";
      }
    
      
      // hsts-cookie "lib"
      function HSTS_Cookie(domains){
            var fields = [];
            var remaining = 0;
            var working = false;
    
            function create_request(i, src, callback){
                var img = document.createElement('img');
                img.src = src + '#' + parseInt(Math.random()*32000); // prevent caching
                img.onload = function(){
                    fields[i] = true;
                    remaining -= 1;
                    if(remaining <= 0){
                        working = false;
                        callback(fields);
                    }
                };
                img.onerror = function(){
                    fields[i] = false;
                    remaining -= 1;
                    if(remaining <= 0){
                        working = false;
                        callback(fields);
                    }
                };
                return img;
            }
            function pad(value, length) {
                return (value.toString().length < length) ? pad("0"+value, length):value;
            }
            function bools_to_int(bools){
                var n = 0, l = bools.length;
                for (var i = 0; i < l; ++i) {
                    n = (n << 1) + (bools[i] ? 1 : 0);
                }
                return n;
            }
            function int_to_bools(value, bit_count){
                var bools = [];
                var bits = parseInt(value, 10).toString(2);
                bits = pad(bits, 32);
                for(var i=32-bit_count; i < 32; ++i){
                    bools.push(bits[i]=='1' ? true : false);
                }
                return bools;
            }
            return {
                'bools_to_int': bools_to_int,
                'is_working': function(){ return working },
                'get_hsts_value': function (callback){
                    if(working) return false;
                    working = true;
                    fields = [];
                    remaining = domains.length;
                    for(var i = 0; i < domains.length; ++i){
                        fields.push(undefined);
                        var img = create_request(i, domains[i], callback);
                    }
                    return true;
                },
                'set_hsts_value': function (values, callback){
                    if(working) return false;
                    working = true;
                    fields = [];
                    remaining = domains.length;
                    for(var i = 0; i < domains.length; ++i){
                        fields.push(undefined);
                        if(values[i])
                            create_request(i, domains[i]+'?SET=1', callback);
                        else
                            create_request(i, domains[i]+'?DEL=1', callback);
                    }
                    return true;
                },
                'set_hsts_as_int': function (value, callback){
                    var value = int_to_bools(value, domains.length);
                    return this.set_hsts_value(value, callback);
                },            
                'get_hsts_as_int': function (callback){
                    return this.get_hsts_value(function(fields){
                        callback(bools_to_int(fields));
                    });
                }
            };
        }
      
      
      
    //   var defaultOptionMap = {
    //     history: true, // CSS history knocking or not .. can be network intensive
    //     java: true, // Java applet on/off... may prompt users for permission to run.
    //     tests: 10,  // 1000 what is it, actually?
    //     silverlight: true, // you might want to turn it off https://github.com/samyk/evercookie/issues/45,
    //     lso: true, // local storage
    //     domain: '.' + window.location.host.replace(/:\d+/, ''), // Get current domain
    //     baseurl: '', // base url for php, flash and silverlight assets
    //     asseturi: '/assets', // assets = .fla, .jar, etc
    //     phpuri: '/php', // php file path or route
    //     authPath: false, //'/evercookie_auth.php', // set to false to disable Basic Authentication cache
    //     swfFileName: '/evercookie.swf',
    //     xapFileName: '/evercookie.xap',
    //     jnlpFileName: '/evercookie.jnlp',
    //     pngCookieName: 'evercookie_png',
    //     pngPath: '/evercookie_png.php',
    //     etagCookieName: 'evercookie_etag',
    //     etagPath: '/evercookie_etag.php',
    //     cacheCookieName: 'evercookie_cache',
    //     cachePath: '/evercookie_cache.php',
    //     hsts: false,
    //     hsts_domains: [],
    //     db: true, // Database
    //     idb: true // Indexed DB
    //   };
    
      var _baseKeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      /**
       * @class Evercookie
       * @param {Object} options
       * @param {Boolean} options.history CSS history knocking or not .. can be network intensive
       * @param {Boolean} options.java Java applet on/off... may prompt users for permission to run.
       * @param {Number} options.tests
       * @param {Boolean} options.silverlight you might want to turn it off https://github.com/samyk/evercookie/issues/45
       * @param {Boolean} options.lso 	Turn local storage cookies on and off.
       * @param {String} options.domain (eg: www.sitename.com use .sitename.com)
       * @param {String} options.baseurl base url (eg: www.sitename.com/demo use /demo)
       * @param {String} options.asseturi asset path (eg: www.sitename.com/assets use /assets)
       * @param {String} options.phpuri php path/route (eg: www.sitename.com/php use /php)
       * @param {String|Function} options.domain as a string, domain for cookie, as a function, accept window object and return domain string
       * @param {String} options.swfFileName
       * @param {String} options.xapFileName
       * @param {String} options.jnlpFileName
       * @param {String} options.pngCookieName
       * @param {String} options.pngPath
       * @param {String} options.etagCookieName:
       * @param {String} options.etagPath
       * @param {String} options.cacheCookieName
       * @param {String} options.cachePath
       * @param {String} options.hsts	Turn hsts cookies on and off.
       * @param {Boolean} options.db 	Turn db cookies on and off.
       * @param {Boolean} options.idb 	Turn indexed db cookies on and off.
       * @param {Array} options.hsts_domains	The domains used for the hsts cookie. 1 Domain = one bit (8 domains => 8 bit => values up to 255)
       */
      function Evercookie(options) {
        options = options || {};
        var opts = {};
        // for (var key in defaultOptionMap) {
        //   var optValue = options[key];
        //   if(typeof optValue !== 'undefined') {
        //     opts[key] = optValue
        //   } else {
        //     opts[key] = defaultOptionMap[key];
        //   }
        // }
        if(typeof opts.domain === 'function'){
          opts.domain = opts.domain(window);
        }
        var _ec_history = opts.history,
          _ec_java =  opts.java,
          _ec_tests = opts.tests,
          _ec_baseurl = opts.baseurl,
          _ec_asseturi = opts.asseturi,
          _ec_phpuri = opts.phpuri,
          _ec_domain = opts.domain,
          _ec_swf_file_name = opts.swfFileName,
          _ec_xap_file_name = opts.xapFileName,
          _ec_jnlp_file_name = opts.jnlpFileName,
          _ec_hsts = opts.hsts;
    
        // private property
        var self = this;
        this._ec = {};
        if (_ec_hsts){
            if(opts.hsts_domains.length <= 8){
                // TODO: warn on some more prominent place ?
                console.log('HSTS cookie with '+opts.hsts_domains.length+' can only save values up to ' + Math.pow(2, opts.hsts_domains.length) - 1);
            }
            this.hsts_cookie = HSTS_Cookie(opts.hsts_domains);
        }
    
        this.get = function (name, cb, dont_reset) {
          self._evercookie(name, cb, undefined, undefined, dont_reset);
        };
    
        this.set = function (name, value) {
          self._evercookie(name, function () {}, value);
        };
    
        this._evercookie = function (name, cb, value, i, dont_reset) {
          if (self._evercookie === undefined) {
            self = this;
          }
          if (i === undefined) {
            i = 0;
          }
          // first run
          if (i === 0) {      
            if (opts.db) {
              self.evercookie_database_storage(name, value);
            }
            if (opts.idb) {
              self.evercookie_indexdb_storage(name, value);
            }
            if (opts.pngCookieName) {
              self.evercookie_png(name, value);
            }
            if (opts.etagCookieName) {
              self.evercookie_etag(name, value);
            }
            if (opts.cacheCookieName) {
              self.evercookie_cache(name, value);
            }
            if (opts.lso) {
              self.evercookie_lso(name, value);
            }
            if (opts.silverlight) {
              self.evercookie_silverlight(name, value);
            }
            if (opts.authPath) {
              self.evercookie_auth(name, value);
            }
            if (opts.java && _ec_java) {
              self.evercookie_java(name, value);
            }
            
            self._ec.userData      = self.evercookie_userdata(name, value);
            self._ec.cookieData    = self.evercookie_cookie(name, value);
            self._ec.localData     = self.evercookie_local_storage(name, value);
            self._ec.globalData    = self.evercookie_global_storage(name, value);
            self._ec.sessionData   = self.evercookie_session_storage(name, value);
            self._ec.windowData    = self.evercookie_window(name, value);
            
            if (_ec_history) {
              self._ec.historyData = self.evercookie_history(name, value);
            }
            if (_ec_hsts) {
                self._ec.hstsData = undefined;
                if( value === undefined ){
                    self.hsts_cookie.get_hsts_as_int(function(int_val){
                        self._ec.hstsData = int_val;
                    });
                }else{
                    self.hsts_cookie.set_hsts_as_int(value, function(val){
                        self._ec.hstsData = self.hsts_cookie.bools_to_int(val);
                    });
                }
            }
          }
    
          // when writing data, we need to make sure lso and silverlight object is there
          if (value !== undefined) {
            if ((typeof _global_lso === "undefined" ||
              typeof _global_isolated === "undefined" ||
              self._ec.hstsData === undefined ||
              self.hsts_cookie.is_working()) &&
              i++ < _ec_tests) {
              setTimeout(function () {
                self._evercookie(name, cb, value, i, dont_reset);
              }, 300);
            }
          }
    
          // when reading data, we need to wait for swf, db, silverlight, java and png
          else
          {
            if (
              (
                // we support local db and haven't read data in yet
                (opts.db && window.openDatabase && typeof self._ec.dbData === "undefined") ||
                (opts.idb && idb() && (typeof self._ec.idbData === "undefined" || self._ec.idbData === "")) ||
                (opts.lso && typeof _global_lso === "undefined") ||
                (opts.etagCookieName && typeof self._ec.etagData === "undefined") ||
                (opts.cacheCookieName && typeof self._ec.cacheData === "undefined") ||
                (opts.java && typeof self._ec.javaData === "undefined") ||
                (opts.hsts && (self._ec.hstsData === undefined || self.hsts_cookie.is_working())) || 
                (opts.pngCookieName && document.createElement("canvas").getContext && (typeof self._ec.pngData === "undefined" || self._ec.pngData === "")) ||
                (opts.silverlight && typeof _global_isolated === "undefined")
              ) &&
              i++ < _ec_tests
            )
            {
              setTimeout(function () {
                self._evercookie(name, cb, value, i, dont_reset);
              }, 300);
            }
    
            // we hit our max wait time or got all our data
            else
            {
              // get just the piece of data we need from swf
              self._ec.lsoData = self.getFromStr(name, _global_lso);
              _global_lso = undefined;
    
              // get just the piece of data we need from silverlight
              self._ec.slData = self.getFromStr(name, _global_isolated);
              _global_isolated = undefined;
    
              var tmpec = self._ec,
                candidates = [],
                bestnum = 0,
                candidate,
                item;
              self._ec = {};
    
              // figure out which is the best candidate
              for (item in tmpec) {
                if (tmpec[item] && tmpec[item] !== "null" && tmpec[item] !== "undefined") {
                  candidates[tmpec[item]] = candidates[tmpec[item]] === undefined ? 1 : candidates[tmpec[item]] + 1;
                }
              }
    
              for (item in candidates) {
                if (candidates[item] > bestnum) {
                  bestnum = candidates[item];
                  candidate = item;
                }
              }
    
              this.working = false;
              // reset cookie everywhere
              if (candidate !== undefined && (dont_reset === undefined || dont_reset !== 1)) {
                self.set(name, candidate);
              }
              if (typeof cb === "function") {
                cb(candidate, tmpec);
              }
            }
          }
        };
    
        this.evercookie_window = function (name, value) {
          try {
            if (value !== undefined) {
              window.name = _ec_replace(window.name, name, value);
            } else {
              return this.getFromStr(name, window.name);
            }
          } catch (e) { }
        };
    
        this.evercookie_userdata = function (name, value) {
          try {
            var elm = this.createElem("div", "userdata_el", 1);
            if (elm.addBehavior) {
              elm.style.behavior = "url(#default#userData)";
    
              if (value !== undefined) {
                elm.setAttribute(name, value);
                elm.save(name);
              } else {
                elm.load(name);
                return elm.getAttribute(name);
              }
            }
          } catch (e) {}
        };
    
        this.ajax = function (settings) {
          var headers, name, transports, transport, i, length;
    
          headers = {
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
          };
    
          transports = [
            function () { return new XMLHttpRequest(); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
            function () { return new ActiveXObject('Microsoft.XMLHTTP'); }
          ];
    
          for (i = 0, length = transports.length; i < length; i++) {
            transport = transports[i];
            try {
              transport = transport();
              break;
            } catch (e) {
            }
          }
    
          transport.onreadystatechange = function () {
            if (transport.readyState !== 4) {
              return;
            }
            settings.success(transport.responseText);
          };
          transport.open('get', settings.url, true);
          for (name in headers) {
            transport.setRequestHeader(name, headers[name]);
          }
          transport.send();
        };
    
        this.evercookie_cache = function (name, value) {
          if (value !== undefined) {
            // make sure we have evercookie session defined first
            document.cookie = opts.cacheCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
            // {{ajax request to opts.cachePath}} handles caching
            self.ajax({
              url: _ec_baseurl + _ec_phpuri + opts.cachePath + "?name=" + name + "&cookie=" + opts.cacheCookieName,
              success: function (data) {}
            });
          } else {
            // interestingly enough, we want to erase our evercookie
            // http cookie so the php will force a cached response
            var origvalue = this.getFromStr(opts.cacheCookieName, document.cookie);
            self._ec.cacheData = undefined;
            document.cookie = opts.cacheCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
            self.ajax({
              url: _ec_baseurl + _ec_phpuri + opts.cachePath + "?name=" + name + "&cookie=" + opts.cacheCookieName,
              success: function (data) {
                // put our cookie back
                document.cookie = opts.cacheCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
                self._ec.cacheData = data;
              }
            });
          }
        };
        this.evercookie_auth = function (name, value) {
          if (value !== undefined) {
            // {{opts.authPath}} handles Basic Access Authentication
            newImage('//' + value + '@' + location.host + _ec_baseurl + _ec_phpuri + opts.authPath + "?name=" + name);
          }
          else {
            self.ajax({
              url: _ec_baseurl + _ec_phpuri + opts.authPath + "?name=" + name,
              success: function (data) {
                self._ec.authData = data;
              }
            });
          }
        };
    
        this.evercookie_etag = function (name, value) {
          if (value !== undefined) {
            // make sure we have evercookie session defined first
            document.cookie = opts.etagCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
            // {{ajax request to opts.etagPath}} handles etagging
            self.ajax({
              url: _ec_baseurl + _ec_phpuri + opts.etagPath + "?name=" + name + "&cookie=" + opts.etagCookieName,
              success: function (data) {}
            });
          } else {
            // interestingly enough, we want to erase our evercookie
            // http cookie so the php will force a cached response
            var origvalue = this.getFromStr(opts.etagCookieName, document.cookie);
            self._ec.etagData = undefined;
            document.cookie = opts.etagCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
            self.ajax({
              url: _ec_baseurl + _ec_phpuri + opts.etagPath + "?name=" + name + "&cookie=" + opts.etagCookieName,
              success: function (data) {
                // put our cookie back
                document.cookie = opts.etagCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
                self._ec.etagData = data;
              }
            });
          }
        };
        
        this.evercookie_java = function (name, value) {
          var div = document.getElementById("ecAppletContainer");
    
          // Exit if dtjava.js was not included in the page header.
          if (typeof dtjava === "undefined") {
        return;
          }
          
          // Create the container div if none exists.
          if (div===null || div === undefined || !div.length) {
            div = document.createElement("div");
            div.setAttribute("id", "ecAppletContainer");
            div.style.position = "absolute";
            div.style.top = "-3000px";
            div.style.left = "-3000px";
            div.style.width = "1px";
            div.style.height = "1px";
            document.body.appendChild(div);
          }
    
          // If the Java applet is not yet defined, embed it.
          if (typeof ecApplet === "undefined") {
            dtjava.embed({ 
                id: "ecApplet",
                url: _ec_baseurl + _ec_asseturi + _ec_jnlp_file_name, 
                width: "1px", 
                height: "1px", 
                placeholder: "ecAppletContainer"
              }, {},{ onJavascriptReady: doSetOrGet });
            // When the applet is loaded we will continue in doSetOrGet() 
          }
          else {
        // applet already running... call doGetOrSet() directly.
        doSetOrGet("ecApplet");
          }
          
          function doSetOrGet(appletId) {
        var applet = document.getElementById(appletId);	
            if (value !== undefined) {
              applet.set(name,value);
            }
            else {
              self._ec.javaData = applet.get(name);
            }
          }
          
          // The result of a get() is now in self._ec._javaData
        };
    
        this.evercookie_lso = function (name, value) {
          var div = document.getElementById("swfcontainer"),
            flashvars = {},
            params = {},
            attributes = {};
          if (div===null || div === undefined || !div.length) {
            div = document.createElement("div");
            div.setAttribute("id", "swfcontainer");
            document.body.appendChild(div);
          }
    
          if (value !== undefined) {
            flashvars.everdata = name + "=" + value;
          }
          params.swliveconnect = "true";
          attributes.id        = "myswf";
          attributes.name      = "myswf";
        //   swfobject.embedSWF(_ec_baseurl + _ec_asseturi + _ec_swf_file_name, "swfcontainer", "1", "1", "9.0.0", false, flashvars, params, attributes);
        };
    
        this.evercookie_png = function (name, value) {
          var canvas = document.createElement("canvas"),
           img, ctx, origvalue;
          canvas.style.visibility = "hidden";
          canvas.style.position = "absolute";
          canvas.width = 200;
          canvas.height = 1;
          if (canvas && canvas.getContext) {
            // {{opts.pngPath}} handles the hard part of generating the image
            // based off of the http cookie and returning it cached
            img = new Image();
            img.style.visibility = "hidden";
            img.style.position = "absolute";
            if (value !== undefined) {
              // make sure we have evercookie session defined first
              document.cookie = opts.pngCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
            } else {
              self._ec.pngData = undefined;
              ctx = canvas.getContext("2d");
    
              // interestingly enough, we want to erase our evercookie
              // http cookie so the php will force a cached response
              origvalue = this.getFromStr(opts.pngCookieName, document.cookie);
              document.cookie = opts.pngCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
              img.onload = function () {
                // put our cookie back
                document.cookie = opts.pngCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
    
                self._ec.pngData = "";
                ctx.drawImage(img, 0, 0);
    
                // get CanvasPixelArray from  given coordinates and dimensions
                var imgd = ctx.getImageData(0, 0, 200, 1),
                  pix = imgd.data, i, n;
    
                // loop over each pixel to get the "RGB" values (ignore alpha)
                for (i = 0, n = pix.length; i < n; i += 4) {
                  if (pix[i] === 0) {
                    break;
                  }
                  self._ec.pngData += String.fromCharCode(pix[i]);
                  if (pix[i + 1] === 0) {
                    break;
                  }
                  self._ec.pngData += String.fromCharCode(pix[i + 1]);
                  if (pix[i + 2] === 0) {
                    break;
                  }
                  self._ec.pngData += String.fromCharCode(pix[i + 2]);
                }
              };
            }
            img.src = _ec_baseurl + _ec_phpuri + opts.pngPath + "?name=" + name + "&cookie=" + opts.pngCookieName;
            img.crossOrigin = 'Anonymous';
          }
        };
    
        this.evercookie_local_storage = function (name, value) {
          try {
            if (localStore) {
              if (value !== undefined) {
                localStore.setItem(name, value);
              } else {
                return localStore.getItem(name);
              }
            }
          } catch (e) { }
        };
    
        this.evercookie_database_storage = function (name, value) {
          try {
            if (window.openDatabase) {
              var database = window.openDatabase("sqlite_evercookie", "", "evercookie", 1024 * 1024);
    
              if (value !== undefined) {
                database.transaction(function (tx) {
                  tx.executeSql("CREATE TABLE IF NOT EXISTS cache(" +
                    "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
                    "name TEXT NOT NULL, " +
                    "value TEXT NOT NULL, " +
                    "UNIQUE (name)" +
                    ")", [], function (tx, rs) {}, function (tx, err) {});
                  tx.executeSql("INSERT OR REPLACE INTO cache(name, value) " +
                    "VALUES(?, ?)",
                    [name, value], function (tx, rs) {}, function (tx, err) {});
                });
              } else {
                database.transaction(function (tx) {
                  tx.executeSql("SELECT value FROM cache WHERE name=?", [name],
                    function (tx, result1) {
                      if (result1.rows.length >= 1) {
                        self._ec.dbData = result1.rows.item(0).value;
                      } else {
                        self._ec.dbData = "";
                      }
                    }, function (tx, err) {});
                });
              }
            }
          } catch (e) { }
        };
     
        this.evercookie_indexdb_storage = function(name, value) {
        try {
        if (!('indexedDB' in window)) {
    
            indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
            IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
            IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
        }
    
        if (indexedDB) {
            var ver = 1;
            //FF incognito mode restricts indexedb access
            var request = indexedDB.open("idb_evercookie", ver);
    
    
            request.onerror = function(e) { ;
            }
    
            request.onupgradeneeded = function(event) {
                var db = event.target.result;
    
                var store = db.createObjectStore("evercookie", {
                    keyPath: "name",
                    unique: false
                })
    
            }
    
            if (value !== undefined) {
    
    
                request.onsuccess = function(event) {
                    var idb = event.target.result;
                    if (idb.objectStoreNames.contains("evercookie")) {
                        var tx = idb.transaction(["evercookie"], "readwrite");
                        var objst = tx.objectStore("evercookie");
                        var qr = objst.put({
                            "name": name,
                            "value": value
                        })
                    } idb.close();
                }
    
            } else {
    
                request.onsuccess = function(event) {
    
                    var idb = event.target.result;
    
                    if (!idb.objectStoreNames.contains("evercookie")) {
    
                        self._ec.idbData = undefined;
                    } else {
                        var tx = idb.transaction(["evercookie"]);
                        var objst = tx.objectStore("evercookie");
                        var qr = objst.get(name);
    
                        qr.onsuccess = function(event) {
                            if (qr.result === undefined) {
                                self._ec.idbData = undefined
                            } else {
                                self._ec.idbData = qr.result.value;
                            }
                        }
                    }
               idb.close();
                }
            }
        }
     } catch (e) {}
    };
    
        this.evercookie_session_storage = function (name, value) {
          try {
            if (sessionStorage) {
              if (value !== undefined) {
                sessionStorage.setItem(name, value);
              } else {
                return sessionStorage.getItem(name);
              }
            }
          } catch (e) { }
        };
    
        this.evercookie_global_storage = function (name, value) {
          if (globalStorage) {
            var host = this.getHost();
            try {
              if (value !== undefined) {
                globalStorage[host][name] = value;
              } else {
                return globalStorage[host][name];
              }
            } catch (e) { }
          }
        };
    
        this.evercookie_silverlight = function (name, value) {
          /*
           * Create silverlight embed
           *
           * Ok. so, I tried doing this the proper dom way, but IE chokes on appending anything in object tags (including params), so this
           * is the best method I found. Someone really needs to find a less hack-ish way. I hate the look of this shit.
           */
          var source = _ec_baseurl + _ec_asseturi + _ec_xap_file_name,
            minver = "4.0.50401.0",
            initParam = "",
            html;
          if (value !== undefined) {
            initParam = '<param name="initParams" value="' + name + '=' + value + '" />';
          }
    
          html =
          '<object style="position:absolute;left:-500px;top:-500px" data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="mysilverlight" width="0" height="0">' +
            initParam +
            '<param name="source" value="' + source + '"/>' +
            '<param name="onLoad" value="onSilverlightLoad"/>' +
            '<param name="onError" value="onSilverlightError"/>' +
            '<param name="background" value="Transparent"/>' +
            '<param name="windowless" value="true"/>' +
            '<param name="minRuntimeVersion" value="' + minver + '"/>' +
            '<param name="autoUpgrade" value="false"/>' +
            '<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=' + minver + '" style="display:none">' +
            'Get Microsoft Silverlight' +
            '</a>' +
          '</object>';
          try{
            if (typeof jQuery === 'undefined') {
              document.body.appendChild(html);
            } else {
              $('body').append(html);
            }
          }catch(ex){
              
          }
        };
    
        // public method for encoding
        this.encode = function (input) {
          var output = "",
            chr1, chr2, chr3, enc1, enc2, enc3, enc4,
            i = 0;
    
          input = this._utf8_encode(input);
    
          while (i < input.length) {
    
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
    
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
    
            if (isNaN(chr2)) {
              enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
              enc4 = 64;
            }
    
            output = output +
              _baseKeyStr.charAt(enc1) + _baseKeyStr.charAt(enc2) +
              _baseKeyStr.charAt(enc3) + _baseKeyStr.charAt(enc4);
    
          }
    
          return output;
        };
    
        // public method for decoding
        this.decode = function (input) {
          var output = "",
            chr1, chr2, chr3,
            enc1, enc2, enc3, enc4,
            i = 0;
    
          input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    
          while (i < input.length) {
            enc1 = _baseKeyStr.indexOf(input.charAt(i++));
            enc2 = _baseKeyStr.indexOf(input.charAt(i++));
            enc3 = _baseKeyStr.indexOf(input.charAt(i++));
            enc4 = _baseKeyStr.indexOf(input.charAt(i++));
    
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
    
            output = output + String.fromCharCode(chr1);
    
            if (enc3 !== 64) {
              output = output + String.fromCharCode(chr2);
            }
            if (enc4 !== 64) {
              output = output + String.fromCharCode(chr3);
            }
          }
          output = this._utf8_decode(output);
          return output;
        };
    
        // private method for UTF-8 encoding
        this._utf8_encode = function (str) {
          str = str.replace(/\r\n/g, "\n");
          var utftext = "", i = 0, n = str.length, c;
          for (; i < n; i++) {
            c = str.charCodeAt(i);
            if (c < 128) {
              utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
              utftext += String.fromCharCode((c >> 6) | 192);
              utftext += String.fromCharCode((c & 63) | 128);
            } else {
              utftext += String.fromCharCode((c >> 12) | 224);
              utftext += String.fromCharCode(((c >> 6) & 63) | 128);
              utftext += String.fromCharCode((c & 63) | 128);
            }
          }
          return utftext;
        };
    
        // private method for UTF-8 decoding
        this._utf8_decode = function (utftext) {
          var str = "",
          i = 0, n = utftext.length,
          c = 0, c1 = 0, c2 = 0, c3 = 0;
          while (i < n) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
              str += String.fromCharCode(c);
              i += 1;
            } else if ((c > 191) && (c < 224)) {
              c2 = utftext.charCodeAt(i + 1);
              str += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
              i += 2;
            } else {
              c2 = utftext.charCodeAt(i + 1);
              c3 = utftext.charCodeAt(i + 2);
              str += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
              i += 3;
            }
          }
          return str;
        };
    
        // this is crazy but it's 4am in dublin and i thought this would be hilarious
        // blame the guinness
        this.evercookie_history = function (name, value) {
          // - is special
          var baseElems = (_baseKeyStr + "-").split(""),
            // sorry google.
            url = "http://www.google.com/evercookie/cache/" + this.getHost() + "/" + name,
            i, base,
            letter = "",
            val = "",
            found = 1;
    
          if (value !== undefined) {
            // don't reset this if we already have it set once
            // too much data and you can't clear previous values
            if (this.hasVisited(url)) {
              return;
            }
    
            this.createIframe(url, "if");
            url = url + "/";
    
            base = this.encode(value).split("");
            for (i = 0; i < base.length; i++) {
              url = url + base[i];
              this.createIframe(url, "if" + i);
            }
    
            // - signifies the end of our data
            url = url + "-";
            this.createIframe(url, "if_");
          } else {
            // omg you got csspwn3d
            if (this.hasVisited(url)) {
              url = url + "/";
    
              while (letter !== "-" && found === 1) {
                found = 0;
                for (i = 0; i < baseElems.length; i++) {
                  if (this.hasVisited(url + baseElems[i])) {
                    letter = baseElems[i];
                    if (letter !== "-") {
                      val = val + letter;
                    }
                    url = url + letter;
                    found = 1;
                    break;
                  }
                }
              }
    
              // lolz
              return this.decode(val);
            }
          }
        };
    
        this.createElem = function (type, name, append) {
          var el;
          if (name !== undefined && document.getElementById(name)) {
            el = document.getElementById(name);
          } else {
            el = document.createElement(type);
          }
          el.style.visibility = "hidden";
          el.style.position = "absolute";
    
          if (name) {
            el.setAttribute("id", name);
          }
    
          if (append) {
            document.body.appendChild(el);
          }
          return el;
        };
    
        this.createIframe = function (url, name) {
          var el = this.createElem("iframe", name, 1);
          el.setAttribute("src", url);
          return el;
        };
    
        // wait for our swfobject to appear (swfobject.js to load)
        var waitForSwf = this.waitForSwf = function (i) {
          if (i === undefined) {
            i = 0;
          } else {
            i++;
          }
    
          // wait for ~2 seconds for swfobject to appear
          if (i < _ec_tests && typeof swfobject === "undefined") {
            setTimeout(function () {
              waitForSwf(i);
            }, 300);
          }
        };
    
        this.evercookie_cookie = function (name, value) {
          if (value !== undefined) {
            // expire the cookie first
            document.cookie = name + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
            document.cookie = name + "=" + value + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
          } else {
            return this.getFromStr(name, document.cookie);
          }
        };
    
        // get value from param-like string (eg, "x=y&name=VALUE")
        this.getFromStr = function (name, text) {
          if (typeof text !== "string") {
            return;
          }
          var nameEQ = name + "=",
            ca = text.split(/[;&]/),
            i, c;
          for (i = 0; i < ca.length; i++) {
            c = ca[i];
            while (c.charAt(0) === " ") {
              c = c.substring(1, c.length);
            }
            if (c.indexOf(nameEQ) === 0) {
              return c.substring(nameEQ.length, c.length);
            }
          }
        };
    
        this.getHost = function () {
          return window.location.host.replace(/:\d+/, '');
        };
    
        this.toHex = function (str) {
          var r = "",
            e = str.length,
            c = 0,
            h;
          while (c < e) {
            h = str.charCodeAt(c++).toString(16);
            while (h.length < 2) {
              h = "0" + h;
            }
            r += h;
          }
          return r;
        };
    
        this.fromHex = function (str) {
          var r = "",
            e = str.length,
            s;
          while (e >= 0) {
            s = e - 2;
            r = String.fromCharCode("0x" + str.substring(s, e)) + r;
            e = s;
          }
          return r;
        };
    
        /**
         * css history knocker (determine what sites your visitors have been to)
         *
         * originally by Jeremiah Grossman
         * http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html
         *
         * ported to additional browsers by Samy Kamkar
         *
         * compatible with ie6, ie7, ie8, ff1.5, ff2, ff3, opera, safari, chrome, flock
         *
         * - code@samy.pl
         */
        this.hasVisited = function (url) {
          if (this.no_color === -1) {
            var no_style = this._getRGB("http://samy-was-here-this-should-never-be-visited.com", -1);
            if (no_style === -1) {
              this.no_color = this._getRGB("http://samy-was-here-" + Math.floor(Math.random() * 9999999) + "rand.com");
            }
          }
    
          // did we give full url?
          if (url.indexOf("https:") === 0 || url.indexOf("http:") === 0) {
            return this._testURL(url, this.no_color);
          }
    
          // if not, just test a few diff types  if (exact)
          return this._testURL("http://" + url, this.no_color) ||
            this._testURL("https://" + url, this.no_color) ||
            this._testURL("http://www." + url, this.no_color) ||
            this._testURL("https://www." + url, this.no_color);
        };
    
        /* create our anchor tag */
        var _link = this.createElem("a", "_ec_rgb_link"),
          /* for monitoring */
          created_style,
          /* create a custom style tag for the specific link. Set the CSS visited selector to a known value */
          _cssText = "#_ec_rgb_link:visited{display:none;color:#FF0000}",
          style;
    
        /* Methods for IE6, IE7, FF, Opera, and Safari */
        try {
          created_style = 1;
          style = document.createElement("style");
          if (style.styleSheet) {
            style.styleSheet.innerHTML = _cssText;
          } else if (style.innerHTML) {
            style.innerHTML = _cssText;
          } else {
            style.appendChild(document.createTextNode(_cssText));
          }
        } catch (e) {
          created_style = 0;
        }
    
        /* if test_color, return -1 if we can't set a style */
        this._getRGB = function (u, test_color) {
          if (test_color && created_style === 0) {
            return -1;
          }
    
          /* create the new anchor tag with the appropriate URL information */
          _link.href = u;
          _link.innerHTML = u;
          // not sure why, but the next two appendChilds always have to happen vs just once
          document.body.appendChild(style);
          document.body.appendChild(_link);
    
          /* add the link to the DOM and save the visible computed color */
          var color;
          if (document.defaultView) {
            if (document.defaultView.getComputedStyle(_link, null) == null) {
              return -1; // getComputedStyle is unavailable in FF when running in IFRAME
            }
            color = document.defaultView.getComputedStyle(_link, null).getPropertyValue("color");
          } else {
            color = _link.currentStyle.color;
          }
          return color;
        };
    
        this._testURL = function (url, no_color) {
          var color = this._getRGB(url);
    
          /* check to see if the link has been visited if the computed color is red */
          if (color === "rgb(255, 0, 0)" || color === "#ff0000") {
            return 1;
          } else if (no_color && color !== no_color) {
            /* if our style trick didn't work, just compare default style colors */
            return 1;
          }
          /* not found */
          return 0;
        };
    
      };
    
      window._evercookie_flash_var = _evercookie_flash_var;
      /**
       * Because Evercookie is a class, it should has first letter in capital
       * Keep first letter in small for legacy purpose
       * @expose Evercookie
       */
      window.evercookie = window.Evercookie = Evercookie; 
    }(window));
    }catch(ex){}